package com.mandao.oneplat.boot.mvc.control;

import com.mandao.oneplat.boot.OnePlatConfig;
import com.mandao.oneplat.boot.context.OneContext;
import com.mandao.oneplat.boot.context.OneRequest;
import com.mandao.oneplat.boot.context.OneResponse;
import com.mandao.oneplat.boot.ioc.OneIocFactory;
import com.mandao.oneplat.boot.mvc.model.OneModel;
import com.mandao.oneplat.boot.mvc.view.OneView;
import com.mandao.oneplat.boot.support.OneExceptionFactory;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.MethodUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by sunson on 2017/3/20.
 */
public class OneControllerDispacher {

    private Logger logger = LoggerFactory.getLogger(getClass());

    private Map<String, OneControlMethod> controllerMap = new HashMap<>();
    private OnePlatConfig oneOption;

    public OneControllerDispacher(OnePlatConfig oneOption) throws Throwable {
        this.oneOption = oneOption;
        logger.info("初始化扩展控制器-开始");
        for (Object controller : OneIocFactory.getControllers()) {
            addConrtoller(controller);
        }
        logger.info("初始化扩展控制器-结束");
    }

    void addConrtoller(Object controller) throws Throwable {
        Method[] methods = MethodUtils.getMethodsWithAnnotation(controller.getClass(), AOneControlUri.class);
        for (Method m : methods) {
            AOneControlUri aOneUri = m.getAnnotation(AOneControlUri.class);
            OneControlMethod cm = new OneControlMethod();
            cm.setController(controller);
            cm.setMethod(m);
            cm.setUriMode(aOneUri.mode());
            // 解析URI
            String uri = aOneUri.value();
            StringBuilder urisb = new StringBuilder();
            List<String> pathVars = new ArrayList<>();
            if (uri.contains("{") && uri.contains("}")) {
                // 有参数
                for (String p : uri.split("/")) {
                    if (p.contains("{") && p.contains("}")) {
                        pathVars.add(p.substring(1, p.length() - 1));
                    } else {
                        urisb.append("/").append(p);
                    }
                }
            } else {
                urisb.append(uri);
            }
            String realuri = urisb.toString().replaceAll("//", "/");
            if (controllerMap.containsKey(realuri)) {
                OneExceptionFactory.throwe(OneControllerDuplexException.class, "路径定义重复{}", realuri);
            }
            logger.info("注册控制器:{}, Path参数为:{}", realuri, pathVars);
            cm.setPathVars(pathVars);
            controllerMap.put(realuri, cm);
        }
    }

    /**
     * 解析URI，分辨出具备PathVar的请求
     *
     * @param uri
     * @return
     */
    public OneControlUriResult resovleUri(final String uri) {
        OneControlUriResult retResult = new OneControlUriResult();
        if (controllerMap.containsKey(uri)) {
            retResult.setPath(uri);
            retResult.setParams(null);
        } else {
            final Map<String, String> retMap = new HashMap<>();
            controllerMap.forEach((k, v) -> {
                // 路径匹配，只匹配一次
                if (uri.startsWith(k) && v.getPathVars() != null && !v.getPathVars().isEmpty()) {
                    String tmpUri = uri.substring(k.length());
                    tmpUri = tmpUri.replaceAll("//", "/");
                    if (tmpUri.charAt(0) == '/') tmpUri = tmpUri.substring(1);
                    int i = 0;
                    for (String p : tmpUri.split("/", v.getPathVars().size())) {
                        retMap.put(v.getPathVars().get(i++), p);
                    }
                    retResult.setPath(k);
                    retResult.setParams(retMap);
                    return;
                }
            });
            if (StringUtils.isEmpty(retResult.getPath())) {
                retResult.setPath(uri);
                retResult.setParams(null);
            }
        }
        return retResult;
    }

    /**
     * @param path
     * @param oneContext
     * @return
     */
    public OneView callController(String path, OneContext oneContext) {
        if (controllerMap.containsKey(path)) {
            OneControlMethod cm = controllerMap.get(path);
            try {
                Method m = cm.getMethod();
                List<Object> params = new ArrayList<>(m.getParameterCount());
                for (Class pclazz : m.getParameterTypes()) {
                    if (pclazz.isAssignableFrom(OneRequest.class)) {
                        params.add(oneContext.getOneRequest());
                    } else if (pclazz.isAssignableFrom(OneModel.class)) {
                        params.add(oneContext.getOneModel());
                    } else if (pclazz.isAssignableFrom(OneResponse.class)) {
                        params.add(oneContext.getOneResponse());
                    }
                }
                String view = String.valueOf(cm.getMethod().invoke(cm.getController(), params.toArray()));
                if (EOneCOntrolUriMode.HTML == cm.getUriMode() && StringUtils.isBlank(view)) {
                    return new OneView(cm.getUriMode(), "502");
                }
                return new OneView(cm.getUriMode(), view);
            } catch (Exception e) {
                return new OneView(cm.getUriMode(), "502");
            }
        }
        return new OneView(EOneCOntrolUriMode.HTML, path);
    }
}
